iT邦幫忙

2023 iThome 鐵人賽

DAY 9
0

今天我們來玩玩 hook 的測試吧,今天會用到的API有

  • renderHook : 生成一個測試用的 TestComponent 並會返回三個參數
    • result : 就是結果,通常會使用到裡面的 current 和 error
    • render : 用來 reRender TestComponent
    • unmount : 可以 uninstall TestComponent
  • act : 如過要操作TestComponent的狀態,就必須靠這位好夥伴

直接進入正題,這邊寫了一個簡單的 customer hook 叫做 useCounter

import { useState } from "react";

function useCounter(initialValue: number = 0 ) {

  const [count, setCount] = useState(initialValue);

  const increment = () => setCount(count => count + 1);
  const decrement = () => setCount(count => count - 1);

  return { count, increment, decrement };
}

export default useCounter;

首先我們來個錯誤示範,我們用一般的方式來使用 useCounter

import useCounter from "./index";

describe("useCounter 測試", () => {
    it("測試 useCounter 初始值", () => {
        const { count } = useCounter(0)
        expect(count).toBe(0);
    });
});

看起來好像沒問題,但當你輸入 npm run test 時,你就會發現

Warning: Invalid hook call.

原因是因為 useState 只能在 function Component 內被呼叫,這時候就是 renderHook 大顯神威的時候了,我們將 renderHook import 進來,透過 renderHook return 的 result 來拿到 useCounter 的狀態,非常好用

import useCounter from "./index";
import { renderHook } from "@testing-library/react";
describe("useCounter 測試", () => {

    it("測試 useCounter 初始值", () => {
        const { result } = renderHook(() => useCounter());
        expect(result.current.count).toBe(0);
    });
    
});

那我們重新輸入 npm run test

PASS  src/utils/hooks/useCounter/index.test.ts

Test Suites: 2 passed, 2 total
Tests:       3 passed, 3 total
Snapshots:   0 total
Time:        5.12 s

那接下來我們使用 increment() 來操作一下狀態,為了操作 TestComponent 我們將 act 也 import 進來,這邊我們要把更新 state 的操作放在 act 的 callback 底下

import useCounter from "./index";
import { renderHook, act} from "@testing-library/react";

describe("useCounter 測試", () => {

    it("測試 useCounter 初始值", () => {
        const { result } = renderHook(() => useCounter());
        expect(result.current.count).toBe(0);
    });

    it("測試 useCounter increment", () => {
        const { result } = renderHook(() => useCounter());
        act(() => {
            result.current.increment();
        });
        expect(result.current.count).toBe(1);
    });

});

npm run test

PASS  src/utils/hooks/useCounter/index.test.ts

Test Suites: 2 passed, 2 total
Tests:       4 passed, 4 total
Snapshots:   0 total
Time:        5.166 s

寫測試時記得要將所有會用到的 case 都加入喔~

import useCounter from "./index";
import { renderHook, act} from "@testing-library/react";
describe("useCounter 測試", () => {

    it("測試 useCounter 初始值", () => {
        const { result } = renderHook(() => useCounter());
        expect(result.current.count).toBe(0);
    });

    it("測試 useCounter 設置初始值", () => {
        const { result } = renderHook(() => useCounter(10));
        expect(result.current.count).toBe(10);
    });

    it("測試 useCounter increment", () => {
        const { result } = renderHook(() => useCounter());
        act(() => {
            result.current.increment();
        });
        expect(result.current.count).toBe(1);
    });

    it("測試 useCounter decrement", () => {
        const { result } = renderHook(() => useCounter(10));
        act(() => {
            result.current.decrement();
        });
        expect(result.current.count).toBe(9);
    });

});

簡單的 hook 測試就先介紹到這邊,明天再繼續寫測試吧!


上一篇
Day 8 - 什麼時候該寫單元測試 & Jest 語法
下一篇
Day 10 - React Hooks 測試 part 2
系列文
React Clean Code And Unit Tests - 利用測試寫出人類看得懂的React程式30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言